#pragma warning(disable: 4786)
#include <algorithm>
#include "MessagePump.h"
#include "ICallback.h"
#include <cassert>



MessageTypeMap MessagePump::m_messageTypes;
MessageList MessagePump::m_messageQueue;
MessageList MessagePump::m_messageIncomingQueue;

//---------------------------------------------------------
//functor for removing delivered messages
bool RemoveIfDelivered(Message* msg)
{
    if(msg->m_delivered)
    {
        delete msg;
        return true;
    }
    return false;
}

//---------------------------------------------------------
MessagePump::MessagePump()
{
//    m_messageTypes.clear();
//    m_messageQueue.clear();
}

//---------------------------------------------------------


void MessagePump::BroadcastMsg(Message* pMsg, MessageType* pMsgType)
{
	MessageRegList::iterator msgReg;
	if (pMsg->m_flag & MODULE_MSG)
	{
		for(msgReg = pMsgType->m_moduleMessageRegList.begin();msgReg != pMsgType->m_moduleMessageRegList.end();++msgReg)
		{
			MessageReg* pmReg = (*msgReg);
			//deliver message by launching ICallback
			if(pmReg->m_Callback)
				pmReg->m_Callback->function(pmReg->m_parent,pMsg);
		}
	}
	else if (pMsg->m_flag & ENTITY_MSG)
	{
		for(msgReg = pMsgType->m_entityMessageRegList.begin();msgReg != pMsgType->m_entityMessageRegList.end();++msgReg)
		{
			MessageReg* pmReg = (*msgReg);
			//deliver message by launching ICallback
			if(pmReg->m_Callback)
				pmReg->m_Callback->function(pmReg->m_parent,pMsg);
		}
	}
}

void MessagePump::UnicastMsg(Message* pMsg, MessageType* pMsgType)
{
	MessageRegList::iterator msgReg;
	if (pMsg->m_flag & MODULE_MSG)
	{// a module message
		for(msgReg = pMsgType->m_moduleMessageRegList.begin();msgReg != pMsgType->m_moduleMessageRegList.end();++msgReg)
		{
			if (pMsg->m_toID == (*msgReg)->m_objectID)
			{
				MessageReg* pmReg = (*msgReg);
				if(pmReg->m_Callback)
					pmReg->m_Callback->function(pmReg->m_parent,pMsg);
			}
		}
	}
	else if (pMsg->m_flag & ENTITY_MSG)
	{//a entity message
		for(msgReg = pMsgType->m_entityMessageRegList.begin();msgReg != pMsgType->m_entityMessageRegList.end();++msgReg)
		{
			if (pMsg->m_toID == (*msgReg)->m_objectID)
			{
				MessageReg* pmReg = (*msgReg);
				if(pmReg->m_Callback)
					pmReg->m_Callback->function(pmReg->m_parent,pMsg);
			}
		}
	}
	else
	{
		assert(0);
	}
}

void MessagePump::DeliverMsg(Message* pMsg)
{
	if ((pMsg->m_flag & BROADCAST_MSG && pMsg->m_flag & UNICAST_MSG) ||
		(pMsg->m_flag & ENTITY_MSG && pMsg->m_flag & MODULE_MSG))
	{
		assert(0 && "msg type is illegal! please check it!");
	}
	//check for registrations
	MessageTypeMap::iterator mType;
	mType = m_messageTypes.find(pMsg->m_typeID);
	if(mType == m_messageTypes.end())
		return;
	MessageType* pmType = (*mType).second;

	if (pMsg->m_flag & BROADCAST_MSG)
	{// a broadcast message
		BroadcastMsg(pMsg,pmType);
	}
	else if (pMsg->m_flag & UNICAST_MSG)
	{// a unicast message
		UnicastMsg(pMsg, pmType);
	}
	else
	{
		assert(0);
	}
	pMsg->m_delivered = true;
}
void MessagePump::Update(int dt)
{
    //move over new incoming messages
    m_messageQueue.merge(m_messageIncomingQueue);
    m_messageIncomingQueue.clear();
    
    if(m_messageQueue.size() == 0)
        return;

    //process messages
    MessageList::iterator msg;
    for(msg = m_messageQueue.begin(); msg != m_messageQueue.end(); ++msg)
    {
        Message* pMsg = (*msg);
        if(pMsg->m_timer > 0)
        {
            //delayed message, decrement timer
            pMsg->m_timer -= dt;
			continue;
        }
        else
        {
			DeliverMsg(pMsg);
        }
    }

    //remove all delivered messages from queue
    MessageList::iterator end    = m_messageQueue.end();
    MessageList::iterator newEnd = std::remove_if(m_messageQueue.begin(),m_messageQueue.end(),RemoveIfDelivered);
    if(newEnd != end)
        m_messageQueue.erase(newEnd,end);
}

//---------------------------------------------------------
void MessagePump::AddMessageToSystem(int type)
{
    //ensure that this type isn't already in the system
    MessageTypeMap::iterator mType;
    mType = m_messageTypes.find(type);
    
    if(mType == m_messageTypes.end())
    {
        MessageType *newType = new MessageType;
        newType->m_typeID = type;
        m_messageTypes[type] = newType;
        newType->m_name = new char[20];
        newType->m_name = GetMessageName(type);
    }
}

//---------------------------------------------------------
char* MessagePump::GetMessageName(int type)
{
    switch(type)
    {
        case MSGTYPE_CHANGE_STATE:
            return "Change State";
        default:
            return "Error Message";
            break;
    }
}
//---------------------------------------------------------
void MessagePump::SendMsg(Message* newMessage)
{
    m_messageIncomingQueue.push_back(newMessage);
}

//---------------------------------------------------------
int MessagePump::RegisterForMessage(int flag, int type, void* parent, int objectID, ICallback& cBack)
{
    //only register once
    MessageTypeMap::iterator mType;
    mType = m_messageTypes.find(type);
    MessageType* pmtype = (*mType).second;
        
    if(mType == m_messageTypes.end())
        return REGISTER_ERROR_MESSAGE_NOT_IN_SYSTEM;

    MessageRegList::iterator msgReg;
	if (flag & MODULE_MSG)
	{
		for(msgReg=(*mType).second->m_moduleMessageRegList.begin();msgReg!=(*mType).second->m_moduleMessageRegList.end();++msgReg)
		{
			if((*msgReg)->m_objectID == objectID)
				return REGISTER_ERROR_ALREADY_REGISTERED;
		}
		MessageReg* newRegistration  = new MessageReg;
		newRegistration->m_parent    = parent;
		newRegistration->m_Callback  = &cBack;
		newRegistration->m_objectID  = objectID;

		(*mType).second->m_moduleMessageRegList.push_back(newRegistration);
	}
	if (flag & ENTITY_MSG)
	{
		for(msgReg=(*mType).second->m_entityMessageRegList.begin();msgReg!=(*mType).second->m_entityMessageRegList.end();++msgReg)
		{
			if((*msgReg)->m_objectID == objectID)
				return REGISTER_ERROR_ALREADY_REGISTERED;
		}
		MessageReg* newRegistration  = new MessageReg;
		newRegistration->m_parent    = parent;
		newRegistration->m_Callback  = &cBack;
		newRegistration->m_objectID  = objectID;

		(*mType).second->m_entityMessageRegList.push_back(newRegistration);
	}

    return REGISTER_MESSAGE_OK;
    
    
}

//---------------------------------------------------------
void MessagePump::UnRegisterForMessage(int flag, int type, int objectID)
{
    //find entry
    MessageTypeMap::iterator mType;
    mType = m_messageTypes.find(type);
    
    if(mType == m_messageTypes.end())
        return;
    
    MessageRegList::iterator msgReg;

	if (flag & MODULE_MSG)
	{
		for(msgReg=(*mType).second->m_moduleMessageRegList.begin();msgReg!=(*mType).second->m_moduleMessageRegList.end();)
		{
			if((*msgReg)->m_objectID == objectID)
			{
				(*mType).second->m_moduleMessageRegList.erase(msgReg);
				delete *msgReg;
			}
			else
				msgReg++;
		}
	}

	if (flag & ENTITY_MSG)
	{
		for(msgReg=(*mType).second->m_entityMessageRegList.begin();msgReg!=(*mType).second->m_entityMessageRegList.end();)
		{
			if((*msgReg)->m_objectID == objectID)
			{
				(*mType).second->m_entityMessageRegList.erase(msgReg);
				delete *msgReg;
			}
			else
				msgReg++;
		}
	}

}

//---------------------------------------------------------
void MessagePump::UnRegisterAll(int flag, int objectID)
{
    MessageTypeMap::iterator mType;
    for(mType = m_messageTypes.begin();mType != m_messageTypes.end(); ++mType)
    {
		UnRegisterForMessage(flag, mType->first, objectID);
    }
}